/*
 * Copyright (c) 2009-2010 HIT Microelectronic Center
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * Authors: Gou Pengfei
 *
 * Date: Aug. 2010
 *
 */

#include <algorithm>
#include <cstring>

#include "base/types.hh"
#include "base/trace.hh"
#include "config/the_isa.hh"
#include "cpu/edge/atomic/base_atomic_block.hh"
#include "sim/byteswap.hh"
#include "base/loader/symtab.hh"


template<class Impl>
BaseAtomicEdgeBlock<Impl>::BaseAtomicEdgeBlock(BlockID blockID, CPU * cpu)
    : cpu(cpu), blockID(blockID)
{
    initVars();
}

template<class Impl>
BaseAtomicEdgeBlock<Impl>::~BaseAtomicEdgeBlock()
{

#ifndef NDEBUG
    --cpu->blockcount;

    DPRINTF(EdgeBlockCount,
        "EdgeBlock: [Bid:%lli] destroyed. Blockcount for %s = %i\n",
        blockID, cpu->name(), cpu->blockcount);
#endif

}

template<class Impl>
void
BaseAtomicEdgeBlock<Impl>::initVars()
{
    flags.set(IsSpeculative); // Inst block is initialed as speculative.
    numWrites=0;
    numReads = 0;
    numStores =0;
    numLoads = 0;
    numTotalInsts = 0;
    numNopInsts = 0;
    numMemRefs = 0;
    numControlInsts = 0;

    numHeadNops = 0;
    numConstInsts = 0;
    numTestInsts = 0;
    numReadWrites = 0;
    numDirectControls = 0;
    numIndirectControls = 0;
    numMoves = 0;
    numNullifies = 0;
    numPredOnTrues = 0;
    numPredOnFalses = 0;

    #ifndef NDEBUG
    ++cpu->blockcount;

    if (cpu->blockcount > 150) {
        assert(cpu->blockcount <= 150);
    }

    DPRINTF(EdgeBlockCount,
        "EdgeBlock: [Bid:%lli] created. Blockcount for %s = %i\n",
        blockID, cpu->name(), cpu->blockcount);
    #endif
}

template<class Impl>
typename BaseAtomicEdgeBlock<Impl>::ListIt
BaseAtomicEdgeBlock<Impl>::addInst(DynInstPtr inst_ptr)
{
    assert( blockID == inst_ptr->getBlockID());

    // Every inst should be added into instList including ld/st.
    instList.push_back(inst_ptr);

    if ( inst_ptr->isMemRef() ) {

        // Ld/st insts will be added into this priority queue
        // in lsid order.
        memRefInsts.push(inst_ptr);

        DPRINTF(EdgeBlock, "Adding load/store insts[lsid:%i].\n",
            inst_ptr->staticInst->getLSID());
    }

    DPRINTF(EdgeBlock,"tid[%i] -- PC:%#x\n ",
                     tid, inst_ptr->readPC());
    DPRINTF(EdgeBlock, "Blcok ID: %lli -- Chunk ID: %lli -- Inst ID: %lli\n",
                     inst_ptr->getBlockID(), inst_ptr->getChunkID(), inst_ptr->getInstID() );
    DPRINTF(EdgeBlock, "Instruction is: %s\n\n",
                     inst_ptr->staticInst->disassemble(inst_ptr->readPC()));

    return (--instList.end());

}

template<class Impl>
typename BaseAtomicEdgeBlock<Impl>::DynInstPtr
BaseAtomicEdgeBlock<Impl>::getInst()
{
    assert(!instList.empty());
    DynInstPtr inst = instList.front();
    instList.pop_front();
    return inst;

}

template<class Impl>
typename BaseAtomicEdgeBlock<Impl>::DynInstPtr
BaseAtomicEdgeBlock<Impl>::getMemRefInst()
{
    assert(!memRefInsts.empty());
    DynInstPtr inst = memRefInsts.top();
    memRefInsts.pop();
    return inst;
}

template<class Impl>
void
BaseAtomicEdgeBlock<Impl>::setStartPC(Addr pc)
{
    assert((pc&TheISA::ChunkOffset) == 0);
    startAddr = pc;
}

template<class Impl>
Addr
BaseAtomicEdgeBlock<Impl>::getStartPC()
{
    return startAddr;
}

template<class Impl>
uint64_t
BaseAtomicEdgeBlock<Impl>::getNumInst() const
{
    assert(instList.size() == numTotalInsts );
    return numTotalInsts;
}

template<class Impl>
uint64_t
BaseAtomicEdgeBlock<Impl>::getNumMemRefInst() const
{
    assert(memRefInsts.size() == numMemRefs);
    return numMemRefs;
}

template<class Impl>
void
BaseAtomicEdgeBlock<Impl>::removeAllInsts()
{
    // Clean() or dumpOutput() should 
    // be called before this.
    assert(storeInsts.empty());
    assert(writeInsts.empty());
    
    DPRINTF(EdgeBlock, "Removing insts in block %i\n", blockID);
    //if (instList.empty())
        //return;

    DPRINTF(EdgeBlock, "Block size:%i\nOutput stores:%i\nOutput writes:%i\n",
                                                            instList.size(),
                                                            storeInsts.size(),
                                                            writeInsts.size());

    numWrites=0;
    numReads = 0;
    numStores =0;
    numTotalInsts = 0;
    numMemRefs = 0;
    numControlInsts = 0;

    instList.clear();

    // Queues have no clear() method.
    while(!memRefInsts.empty()){
        memRefInsts.pop();
    }

    //while(!storeInsts.empty()){
        //storeInsts.pop();
    //}

    //while(!writeInsts.empty()){
        //writeInsts.pop();
    //}

/*
    ListIt it = instList.begin();
    while( it != instList.end())
    {
        instList.erase(it);
    it++;
    }
    */
}

template<class Impl>
void
BaseAtomicEdgeBlock<Impl>::clean()
{
    while(!storeInsts.empty()){
        storeInsts.pop();
    }

    while(!writeInsts.empty()){
        writeInsts.pop();
    }
}

template<class Impl>
void
BaseAtomicEdgeBlock<Impl>::dumpOutput()
{
#ifndef NDEBUG
    std::string sym_str;
    Addr sym_addr;
    debugSymbolTable->findNearestSymbol(this->getStartPC(),
                                                                                                                             sym_str, sym_addr);

    DPRINTFR(EdgeBlockOutput, "PC %#x\n.BLOCK %s\n", this->getStartPC(), sym_str.c_str());

    while( !storeInsts.empty() ){
        DynInstPtr store = *(storeInsts.top());

        if ( store->isNullified() ){
            storeInsts.pop();
            continue;
        }

        assert(store->effAddrValid);
        DPRINTFR( EdgeBlockOutput, "M[%#x,%i] %#x\n", store->effAddr,
                                       store->memAccSize, store->getIntIQOperand(1) );
        storeInsts.pop();
    }

    while( !writeInsts.empty() ){
        DynInstPtr write = *(writeInsts.top());

        if( write->isNullified() ){
            writeInsts.pop();
            continue;
        }

        DPRINTFR( EdgeBlockOutput, "G[%i] %#x\n", write->getMappedDestReg(0),
                                        write->getIntIQOperand(0));
        writeInsts.pop();
    }
#else
    panic("DumpOutput is not supported in non-debug mode. Recompile M5 with DEBUG-mode enable.\n");
#endif
}

